import React, { useEffect, useRef } from 'react';
import { useInterval, useMemoizedFn, useSafeState, useSetState } from 'ahooks';
import { Context } from '../utils/store';
import { useEventListener } from 'ahooks';
import styles from '../style/Provider.module.scss';
import VIDEOLOGO from '@/assets/videologo.png'
import RenderJudge from '@/components/RenderJudge';

// 判断是否支持画中画
const supportsWebKitPresentationMode = (video) => {
  // Check if Safari supports PiP, and is not on mobile (other than iPad)
  // iPhone safari appears to "support" PiP through the check, however PiP does not function
  const notMobile = /iPhone|iPod/.test(navigator.userAgent) === false;
  return video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === 'function' && notMobile;
};

// 重试次数
const RETRY_NUMBER = 3;
// 重试间隔
const RETRY_INTERVAL = 2000;

const Provider = ({ data = [], status: statusProps, methods: methodsProps, volume: defaultVolume, children, videoBackList = [] }) => {
  const isVideoBack = videoBackList?.length > 0
  const [flvInstance, setFlvInstance] = useSafeState(null);
  const [hlsInstance, setHlsInstance] = useSafeState(null);
  const [dataIndex, setIndex] = useSafeState(0);
  const [status, setStatus] = useSetState({
    // 是否正在播放
    played: false,
    // 是否暂停
    paused: true,
    loading: true,
    volume: defaultVolume,
    pictureInPicture: false,
  });
  // 重试次数和时间
  const [retrySetting, setRetrySetting] = useSetState({ switch: false, number: RETRY_NUMBER, interval: RETRY_INTERVAL });

  const videoRef = useRef();
  const videoDom = videoRef.current;

  // video 开启画中画回调
  const onVideoOpenPictureInPictureHandle = useMemoizedFn(() => setStatus({ pictureInPicture: true }));
  // video 关闭画中画回调
  const onVideoClosePictureInPictureHandle = useMemoizedFn(() => {
    if (!status.destroy) {
      const timer = setTimeout(async () => {
        clearTimeout(timer);
        try {
          await videoDom.play();
        } catch (e) {
          // eslint-disable-next-line no-console
          console.warn(e);
        }
      }, 10);
    }

    return setStatus({ pictureInPicture: false });
  });

  const openPictureInPictureHandle = useMemoizedFn(async () => {
    if (videoDom.requestPictureInPicture && document.pictureInPictureElement !== videoDom) {
      await videoDom.requestPictureInPicture();
    } else if (supportsWebKitPresentationMode(videoDom) && videoDom.webkitPresentationMode !== 'picture-in-picture') {
      await videoDom.webkitSetPresentationMode('picture-in-picture');
    }
  });
  const closePictureInPictureHandle = useMemoizedFn(async () => {
    if (document.exitPictureInPicture && document.pictureInPictureElement === videoDom) {
      await document.exitPictureInPicture();
    } else if (supportsWebKitPresentationMode(videoDom) && videoDom.webkitPresentationMode !== 'inline') {
      await videoDom.webkitSetPresentationMode('inline');
    }
  });
  // 销毁
  const destroyHandle = useMemoizedFn(() => {
    if (videoDom) {
      if (status.pictureInPicture) closePictureInPictureHandle();
      videoDom.pause();
    }

    if (flvInstance) {
      try {
        flvInstance.unload();
        flvInstance.detachMediaElement();
        flvInstance.destroy();
      } catch (e) {
        // eslint-disable-next-line no-console
        console.warn(e);
      }

      setFlvInstance(null);
    }

    if (hlsInstance) {
      try {
        hlsInstance.destroy();
      } catch (e) {
        // eslint-disable-next-line no-console
        console.warn(e);
      }

      setHlsInstance(null);
    }
  });

  // 错误回调  或 单个视频回放结束的回调
  const onErrorHandle = () => {
    if (isVideoBack) {
      //如果是回放，这个方法绑定的是video 播放结束的事件
      const index = dataIndex === videoBackList?.length - 1 ? 0 : dataIndex + 1
      setDataIndexHandle(index)
      return
    }
    // 如果重试次数还有就继续重试
    if (retrySetting.number) return setRetrySetting({ switch: true });
  };
  //直播
  const createLivePlayer = async (index) => {
    const { playUrl, protocolType } = data[index] || {};
    if (!playUrl) return;

    destroyHandle();

    videoRef.current.src = playUrl;

    if (protocolType === 'flv') {
      const { default: FlvJs } = await import(/* webpackChunkName: "mpegts.js" */'mpegts.js');
      if (FlvJs.isSupported()) {
        const ins = FlvJs.createPlayer({
          type: 'flv',
          url: playUrl,
          isLive: true,
          hasAudio: true,
          hasVideo: true,
          config: {
            // 对SourceBuffer进行自动清理
            autoCleanupSourceBuffer: true,
            // 关闭IO隐藏缓冲区
            enableStashBuffer: false,
          },
        });
        ins.attachMediaElement(videoRef.current);
        ins.load();
        ins.on(FlvJs.Events.ERROR, onErrorHandle);

        setFlvInstance(ins);
      }
    } else if (protocolType === 'm3u8') {
      const { default: HlsJs } = await import(/* webpackChunkName: "hls.js" */'hls.js');
      if (HlsJs.isSupported()) {
        const ins = new HlsJs({
          enableWorker: true,
          lowLatencyMode: true,
          backBufferLength: 90,
        });

        ins.loadSource(playUrl);
        ins.attachMedia(videoRef.current);
        ins.on(HlsJs.Events.ERROR, onErrorHandle);

        setHlsInstance(ins);
      }
    }
  }
  //回放
  const createVideoBack = async (index) => {
    const { videoSrc: playUrl } = videoBackList[index] || {};
    if (!playUrl) return;
    destroyHandle();
    videoRef.current.src = playUrl;
    const HLSPlay = ['.m3u8']
    if (HLSPlay.some(v => playUrl.includes(v))) {
      const { default: HlsJs } = await import(/* webpackChunkName: "hls.js" */'hls.js');
      if (HlsJs.isSupported()) {
        const ins = new HlsJs({
          enableWorker: true,
          lowLatencyMode: true,
          backBufferLength: 90,
        });
        ins.loadSource(playUrl);
        ins.attachMedia(videoRef.current);
        ins.on(HlsJs.Events.ERROR, (err) => {
          console.log(err)
        });
        setHlsInstance(ins);
      }
    }
  }
  // 创建播放器实例
  const createPlayerInsHandle = useMemoizedFn(async (index = dataIndex) => {
    isVideoBack ? createVideoBack(index) : createLivePlayer(index)
    try {
      videoRef.current.load();
    } catch (e) {
      // eslint-disable-next-line no-console
      console.warn(e);
    }

    // react 会忽略enterpictureinpicture和leavepictureinpicture事件，所以直接使用代码的方式监听
  });

  useEventListener('enterpictureinpicture', onVideoOpenPictureInPictureHandle, { target: videoRef })

  useEventListener('leavepictureinpicture', onVideoClosePictureInPictureHandle, { target: videoRef })

  // 重试
  useInterval(() => {
    setRetrySetting({ switch: false, number: retrySetting.number - 1 });

    return createPlayerInsHandle(dataIndex);
  }, retrySetting.switch && retrySetting.number ? retrySetting.interval : undefined);

  // 根据索引创建播放器
  const createByIndexHandle = useMemoizedFn((index) => {
    //回放和直播区分
    const playUrl = (isVideoBack ? videoBackList[index]?.videoSrc : data[index]?.playUrl) || ""
    setStatus({ loading: !!playUrl, bufferLoading: false, create: false, destroy: !playUrl });

    setIndex(index);

    setRetrySetting({ switch: false, number: RETRY_NUMBER, interval: RETRY_INTERVAL });
    return createPlayerInsHandle(index);
  });

  // 设置数据索引
  const setDataIndexHandle = useMemoizedFn((v) => {
    if (dataIndex === v) return;

    return createByIndexHandle(v);
  })

  // 刷新操作
  const refreshHandle = useMemoizedFn(() => createByIndexHandle(dataIndex))

  // 创建操作
  const createHandle = useMemoizedFn(() => createByIndexHandle(0))

  useEffect(() => {
    destroyHandle()
  }, [])
  // 音量设置
  const volumeHandle = useMemoizedFn((percentage) => {
    if (!videoDom) return;

    if (percentage) {
      videoDom.volume = percentage;
    } else {
      videoDom.volume = 0;
    }
  })
  // 开关音量
  const switchVolumeHandle = useMemoizedFn(() => {
    if (!videoDom) return;
    const { volume } = videoDom;
    if (volume) return volumeHandle(0);

    return volumeHandle(defaultVolume);
  })

  // 点击播放
  const clickPlayHandle = () => new Promise((resolve) => {
    // 如果静音播放还是没有播放就弹框点击播放
    volumeHandle(defaultVolume);
    // 静音播放按钮去掉，因为这里是点击播放
    setStatus({
      clickPlayVisible: true,
      mutedPlayVisible: false,
    });

    resolve();
  });

  // 静音播放逻辑
  const mutedPlayHandle = () => {
    volumeHandle(0);
    setStatus({
      clickPlayVisible: false,
      mutedPlayVisible: true,
    });
    const playPromise = videoDom.play();
    if (playPromise) {
      // promise
      return playPromise.catch(clickPlayHandle);
    }
    return new Promise((resolve) => {
      const isPaused = videoDom.paused;
      if (isPaused) return clickPlayHandle();

      resolve();
    });
  };

  // 正常播放逻辑
  const normalPlayHandle = () => {
    // 初始化两个不能播放的按钮
    setStatus({
      clickPlayVisible: false,
      mutedPlayVisible: false,
    });

    // 刷新时是主动点击所以设置默认音量
    volumeHandle(status.volume);
    const playPromise = videoDom.play();
    if (playPromise) {
      // promise
      return playPromise.catch(mutedPlayHandle);
    }
    return new Promise((resolve) => {
      const isPaused = videoDom.paused;
      if (isPaused) return mutedPlayHandle();

      resolve();
    });
  };

  // 播放操作
  const playHandle = () => {
    if (!videoDom) return;

    return normalPlayHandle();
  };

  // 暂停操作
  const pauseHandle = useMemoizedFn(() => {
    if (!videoDom) return;

    return videoDom.pause();
  })

  // video暂停回调事件
  const onVideoPauseHandle = () => {
    setStatus({
      paused: true,
      played: false,
    });
  };
  // video播放回调事件
  const onVideoPlayHandle = () => {
    setStatus({
      paused: false,
      played: true,
    });
  };
  // video音量改变回调事件
  const onVideoVolumechangeHandle = () => {
    const { volume } = videoDom;
    setStatus({ volume, muted: !volume });
  };
  // video可以播放时回调事件
  const onVideoCanPlayHandle = () => {
    setStatus({ loading: false, bufferLoading: false, create: true, destroy: false });

    return playHandle();
  };

  // video缓冲中
  const onWaitingHandle = () => setStatus({ bufferLoading: true });

  // 设置弹幕显示方式
  const setDanmakuDisplayHandle = useMemoizedFn((danmakuDisplay) => {
    setStatus({ danmakuDisplay });
    localStorage.setItem(DANMAKU_DISPLAY_KEY, danmakuDisplay);
  })

  // 设置弹幕不透明度(0-100)
  const setDanmakuOpacityHandle = useMemoizedFn((danmakuOpacity) => {
    setStatus({ danmakuOpacity: Math.min(100, Math.floor(danmakuOpacity)) });
    localStorage.setItem(DANMAKU_OPACITY_KEY, danmakuOpacity);
  })

  // 设置弹幕字体大小
  const setDanmakuFontSizeHandle = useMemoizedFn((danmakuFontSize) => setStatus({ danmakuFontSize }))

  const storeValue = {
    instance: videoDom,
    data: {
      index: dataIndex,
      list: data,
      setIndex: setDataIndexHandle,
    },
    status: {
      ...status,
      fullScreen: statusProps.fullScreen,
      ctrlVisible: statusProps.ctrlVisible,
      isVideoBack,
      mobile: statusProps?.mobile
    },
    methods: {
      // 销毁
      destroy: destroyHandle,
      // 创建
      create: createHandle,
      // 刷新
      refresh: refreshHandle,
      // 播放
      play: () => createByIndexHandle(dataIndex),
      // 暂停
      pause: pauseHandle,
      // 声音设置
      volume: volumeHandle,
      // 声音开关
      switchVolume: switchVolumeHandle,
      // 全屏
      fullScreen: methodsProps.fullScreen,
      // 退出全屏
      exitScreen: methodsProps.exitScreen,
      // 开启画中画
      openPictureInPicture: openPictureInPictureHandle,
      // 关闭画中画
      closePictureInPicture: closePictureInPictureHandle,
      // 设置弹幕显示(0：关闭弹幕，1全屏弹幕，2半屏)
      danmakuDisplay: setDanmakuDisplayHandle,
      // 设置弹幕不透明度(0-100)
      danmakuOpacity: setDanmakuOpacityHandle,
      // 设置弹幕字体大小
      danmakuFontSize: setDanmakuFontSizeHandle,
    },
  };

  return (
    <Context.Provider value={storeValue}>
      <div className={styles.container}>
        {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
        <video
          className={styles.video}
          ref={videoRef}
          onPause={onVideoPauseHandle}
          onPlay={onVideoPlayHandle}
          onVolumeChange={onVideoVolumechangeHandle}
          onEnded={onErrorHandle}
          onCanPlay={onVideoCanPlayHandle}
          webkit-playsinline="true"
          playsInline
          x5-playsinline="true"
          x-webkit-airplay="true"
          onWaiting={onWaitingHandle}
        />
        <img src={VIDEOLOGO?.src} alt="" className={statusProps?.mobile ? styles.h5logo : styles.logo} />
      </div>
      {children}
    </Context.Provider>
  );
};

Provider.defaultProps = {
  list: [],
  status: {},
  methods: {},
  volume: 0.7,
};

export default Provider;
